export const getDefaultByNull = (...args) => {
  if (isNotNullArray(args)) {
    for (let i = 0; i < args.length; i++) {
      const val = args[i];
      if (val !== undefined && val !== null) {
        return val;
      }
    }
  }
};

export const getDefaultByEmpty = (...args) => {
  if (isNotNullArray(args)) {
    for (let i = 0; i < args.length; i++) {
      const val = args[i];
      if (val !== undefined && val !== null && val !== '') {
        return val;
      }
    }
  }
};

export const getDefaultByNumber = (...args) => {
  if (isNotNullArray(args)) {
    for (let i = 0; i < args.length; i++) {
      const val = args[i];
      if (val !== undefined && val !== null && val !== '' && !isNaN(val)) {
        return val;
      }
    }
  }
};

export const getDefaultByPositive = (...args) => {
  if (isNotNullArray(args)) {
    for (let i = 0; i < args.length; i++) {
      const val = args[i];
      if (val !== undefined && val !== null && val !== '' && !isNaN(val) && val > 0) {
        return val;
      }
    }
  }
};

export const getDefaultByArray = (...args) => {
  if (isNotNullArray(args)) {
    for (let i = 0; i < args.length; i++) {
      const val = args[i];
      if (val !== undefined && val !== null && val.length > 0) {
        return val;
      }
    }
  }
};

export const isObject = (val) => {
  return typeof val === 'object';
};

export const isArray = (val) => {
  return Object.prototype.toString.call(val) === '[object Array]';
};

export const isDate = (val) => {
  // return val instanceof Date
  // return Object.prototype.toString.call(val) === '[object Date]';
  return val instanceof Date;
};

export const isTrue = (val) => {
  return val !== undefined && val !== null && val === true;
};

export const isFalse = (val) => {
  return val !== undefined && val !== null && val === false;
};

export const isNumber = (val) => {
  return val !== undefined && val !== null && val !== '' && !isNaN(val);
};

export const isNotNull = (val) => {
  return val !== undefined && val !== null;
};

export const isNotEmpty = (val) => {
  return val !== undefined && val !== null && val !== '';
};

export const isNotNullArray = (val) => {
  return val !== undefined && val !== null && isArray(val) && val.length > 0;
};

export const isHtmlElement = (el?: Element) => {
  if (el) {
    return el.nodeType === 1;
  }
  return false;
};

export const hasOwnProperty = (object, key) => {
  return Object.prototype.hasOwnProperty.call(object, key);
};

export const getStringClass = (classes?: string | string[] | Pick<string, boolean>): string => {
  if (classes) {
    if (typeof classes === 'string') {
      return classes as string;
    } else if (isArray(classes)) {
      const classList = classes as string[];
      return classList.join(' ');
    } else if (typeof classes === 'object') {
      const classList = [];
      const classObject = classes as Pick<string, boolean>;
      const keys = Object.keys(classObject);
      for (let i = 0; i < keys.length; i++) {
        const name: string = keys[i];
        if (name) {
          const value: boolean = classObject[name];
          if (value) {
            classList.push(name);
          }
        }
      }
      return classList.join(' ');
    }
  }
  return '';
};

export const getObjectClass = (classes?: string | string[] | Pick<string, boolean>): object => {
  if (classes) {
    let classList;
    if (typeof classes === 'string') {
      classList = classes.split(' ');
    } else if (isArray(classes)) {
      classList = classes as string[];
    } else if (typeof classes === 'object') {
      return classes as object;
    }
    const classObject = {};
    if (classList && classList.length > 0) {
      for (let i = 0; i < classList.length; i++) {
        const name = classList[i];
        if (name) {
          classObject[name] = true;
        }
      }
    }
    return classObject;
  }
  return {};
};

export const getStringStyle = (styles?: string | Pick<string, string>): string => {
  if (styles) {
    if (typeof styles === 'string') {
      return styles as string;
    } else if (typeof styles === 'object') {
      const styleList = [];
      const styleObject = styles as Pick<string, boolean>;
      const keys = Object.keys(styleObject);
      for (let i = 0; i < keys.length; i++) {
        const name: string = keys[i];
        if (name) {
          const value: string = styleObject[name];
          if (value) {
            styleList.push(name + ':' + value);
          }
        }
      }
      return styleList.join(';');
    }
  }
  return '';
};

export const getObjectStyle = (styles?: string | Pick<string, string>): object => {
  if (styles) {
    let styleList;
    if (typeof styles === 'string') {
      styleList = styles.split(' ');
    } else if (typeof styles === 'object') {
      return styles as object;
    }
    const classObject = {};
    if (styleList && styleList.length > 0) {
      for (let i = 0; i < styleList.length; i++) {
        const name = styleList[i];
        if (name) {
          classObject[name] = true;
        }
      }
    }
    return classObject;
  }
  return {};
};

export const getValueByPath = (data: any, path: string | string[], defaultValue?: any): any => {
  if (!data || !path) return defaultValue;

  const keys = typeof path === 'string' ? path.split('.') : path;
  let result = data;

  for (const key of keys) {
    if (isNotNull(result)) {
      if (isArray(result)) {
        // 如果是数组，尝试将key转换为数字索引
        const index = Number(key);
        if (!isNumber(index) && index < result.length) {
          result = result[index];
        } else {
          // 索引无效或超出范围，返回defaultValue
          return defaultValue;
        }
      } else if (result && hasOwnProperty(result, key)) {
        // 对象情况，直接访问属性
        result = result[key];
      } else {
        // 键不存在于对象或数组中，返回defaultValue
        return defaultValue;
      }
    } else {
      // 键不存在于对象或数组中，返回defaultValue
      return defaultValue;
    }
  }

  // 如果result为null或undefined，返回defaultValue
  if (isNotNull(result)) {
    return result;
  }

  return defaultValue;
};

export const setValueByPath = (data: any, path: string | string[], value: any): void => {
  if (!data || !path) return;

  const keys = typeof path === 'string' ? path.split('.') : path;
  let current = data;

  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    if (Array.isArray(current)) {
      const index = parseInt(key, 10);
      if (!isNaN(index) && index >= 0 && index < current.length) {
        current = current[index];
      } else {
        // If the index is out of bounds, create a new array or object
        current[index] = {};
        current = current[index];
      }
    } else if (current && !(key in current)) {
      current[key] = {};
      current = current[key];
    } else {
      current = current[key];
    }
  }

  const lastKey = keys[keys.length - 1];
  if (Array.isArray(current)) {
    const lastIndex = parseInt(lastKey, 10);
    if (!isNaN(lastIndex)) {
      current[lastIndex] = value;
    } else {
      // Handle the case where the last key is not a valid array index
      current[lastKey] = value;
    }
  } else {
    current[lastKey] = value;
  }
};

export const expandData = (target) => {
  const result: any = {};
  const length = arguments.length;
  if (length > 1) {
    const source = arguments[1];
    const keys = Object.keys(target);
    for (let key in keys) {
      // 使用for in会遍历数组所有的可枚举属性，包括原型。
      if (Object.prototype.hasOwnProperty.call(target, key)) {
        // 默认数据
        const val1 = target[key];
        const val2 = source[key];
        if (val2 !== undefined && val2 !== null) {
          if (typeof val2 === 'string') {
            result[key] = val2;
          } else if (typeof val2 === 'boolean') {
            result[key] = val2;
          } else if (typeof val2 === 'number') {
            result[key] = val2;
          } else if (typeof val2 === 'function') {
            result[key] = val2;
          } else if (isArray(val2)) {
            target[key] = val2;
          } else if (isDate(val2)) {
            target[key] = val2;
          } else if (typeof val2 === 'object') {
            if (val1 !== undefined && val1 !== null) {
              result[key] = expandData(val1, val2);
            }
          }
        } else if (val1 !== undefined && val1 !== null) {
          result[key] = val1;
        }
      }
    }
  }
  return result;
};

export const extendData = (target) => {
  for (let i = 1; i < arguments.length; i++) {
    const source = arguments[i];
    const keys = Object.keys(source);
    for (let key in keys) {
      // 使用for in会遍历数组所有的可枚举属性，包括原型。
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        const val = source[key];
        if (val !== undefined && val !== null) {
          if (typeof val === 'string') {
            target[key] = val;
          } else if (typeof val === 'boolean') {
            target[key] = val;
          } else if (typeof val === 'number') {
            target[key] = val;
          } else if (typeof val === 'function') {
            target[key] = val;
          } else if (isArray(val)) {
            target[key] = val;
          } else if (isDate(val)) {
            target[key] = val;
          } else if (typeof val === 'object') {
            target[key] = extendData({}, val);
          }
        }
      }
    }
  }
  return target;
};

export const cloneData = (data) => {
  let obj: any;
  if (data !== undefined && data !== null) {
    if (isArray(data)) {
      obj = [];
      for (let i = 0, len = data.length; i < len; i++) {
        obj.push(cloneData(data[i]));
      }
    } else if (isDate(data)) {
      obj = data;
    } else if (typeof data === 'object') {
      obj = {};
      const keys = Object.keys(data);
      for (let key in keys) {
        const value = data[key];
        if (value !== undefined && value !== null) {
          obj[key] = cloneData(data[key]);
        }
      }
    } else {
      obj = data;
    }
  }
  return obj;
};

export const getUuid = () => {
  return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
};

export const resolveTree = (src, rootIds) => {
  return resolveTreeData(src, 'id', 'parentId', 'children', rootIds);
};

export const resolveTreeData = (src, kidField, pidField, childrenName, rootIds) => {
  const keyId = kidField !== undefined && kidField !== null && kidField !== '' ? kidField : 'id';
  const keyPid = pidField !== undefined && pidField !== null && pidField !== '' ? pidField : 'parentId';
  const keyChild = childrenName !== undefined && childrenName !== null && childrenName !== '' ? childrenName : 'children';
  const hasRoot = rootIds !== undefined && rootIds !== null && rootIds !== '';
  const xRootIds = ',' + rootIds + ',';
  let data = [];

  // 组装数据
  if (data && data.length > 0) {
    const map = {};
    const result = [];
    for (let i = 0; i < data.length; i++) {
      const item = data[i];
      if (item) {
        const id = item[keyId];
        if (id !== undefined && id !== null) {
          item[keyId] = id + '';
          // @ts-ignore
          map[id] = item;
        }
      }
    }
    for (let j = 0; j < data.length; j++) {
      const _item = data[j];
      if (_item) {
        const _pid = _item[keyPid];
        if (_pid !== undefined && _pid !== null && _pid !== '') {
          // 如果设置rootId，那么要更加rootId为顶级ID
          if (hasRoot) {
            if (xRootIds.indexOf(',' + _pid + ',') >= 0) {
              result.push(_item);
            } else {
              // @ts-ignore
              const parent = map[_pid];
              if (parent) {
                let children = parent[keyChild];
                if (!children) children = [];
                children.push(_item);
                parent[keyChild] = children;
              }
            }
          } else {
            // @ts-ignore
            const parent = map[_pid];
            if (parent) {
              let children = parent[keyChild];
              if (!children) children = [];
              children.push(_item);
              parent[keyChild] = children;
            } else {
              result.push(_item);
            }
          }
        }
      }
    }
    return result;
  }
  return [];
};

export const once = <T extends (...args: any[]) => any>(fn: T): T => {
  let called = false;
  return function () {
    if (!called) {
      called = true;
      fn.apply(this, arguments as any);
    }
  } as any;
};
